<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Underscore Test Suite</title>
    <link rel="stylesheet" href="../node_modules/qunitjs/qunit/qunit.css">
  </head>
  <body>
    <div id="qunit"></div>
    <script>
      // Avoid reporting tests to Sauce Labs when script errors occur.
      if (location.port == '9001') {
        window.onerror = function(message) {
          if (window.QUnit) {
            QUnit.config.done.length = 0;
          }
          global_test_results = { 'message': message };
        };
      }
    </script>
    <script src="../node_modules/qunitjs/qunit/qunit.js"></script>
    <script src="../node_modules/qunit-extras/qunit-extras.js"></script>
    <script src="../node_modules/jquery/dist/jquery.js"></script>
    <script src="../node_modules/platform/platform.js"></script>
    <script src="./asset/test-ui.js"></script>
    <script src="../lodash.js"></script>
    <script>
      QUnit.config.asyncRetries = 10;
      QUnit.config.hidepassed = true;
      QUnit.config.excused = {
        'Arrays': {
          'chunk': [
            'defaults to empty array (chunk size 0)'
          ],
          'difference': [
            'can perform an OO-style difference'
          ],
          'drop': [
            'is an alias for rest'
          ],
          'first': [
            'returns an empty array when n <= 0 (0 case)',
            'returns an empty array when n <= 0 (negative case)',
            'can fetch the first n elements',
            'returns the whole array if n > length'
          ],
          'findIndex': [
            'called with context'
          ],
          'findLastIndex': [
            'called with context'
          ],
          'flatten': [
            'supports empty arrays',
            'can flatten nested arrays',
            'works on an arguments object',
            'can handle very deep arrays'
          ],
          'indexOf': [
            "sorted indexOf doesn't uses binary search",
            '0'
          ],
          'initial': [
            'returns all but the last n elements',
            'returns an empty array when n > length',
            'works on an arguments object'
          ],
          'intersection': [
            'can perform an OO-style intersection'
          ],
          'last': [
            'returns an empty array when n <= 0 (0 case)',
            'returns an empty array when n <= 0 (negative case)',
            'can fetch the last n elements',
            'returns the whole array if n > length'
          ],
          'lastIndexOf': [
            'should treat falsey `fromIndex` values, except `0` and `NaN`, as `array.length`',
            'should treat non-number `fromIndex` values as `array.length`',
            '[0,-1,-1]'
          ],
          'object': [
            'an array of pairs zipped together into an object',
            'an object converted to pairs and back to an object'
          ],
          'rest': [
            'returns the whole array when index is 0',
            'returns elements starting at the given index',
            'works on an arguments object'
          ],
          'sortedIndex': [
            '2',
            '3'
          ],
          'tail': [
            'is an alias for rest'
          ],
          'take': [
            'is an alias for first'
          ],
          'uniq': [
            'uses the result of `iterator` for uniqueness comparisons (unsorted case)',
            '`sorted` argument defaults to false when omitted',
            'when `iterator` is a string, uses that key for comparisons (unsorted case)',
            'uses the result of `iterator` for uniqueness comparisons (sorted case)',
            'when `iterator` is a string, uses that key for comparisons (sorted case)',
            'can use falsey pluck like iterator'
          ],
          'union': [
            'can perform an OO-style union'
          ]
        },
        'Chaining': {
          'pop': true,
          'shift': true,
          'splice': true,
          'reverse/concat/unshift/pop/map': [
            'can chain together array functions.'
          ]
        },
        'Collections': {
          'lookupIterator with contexts': true,
          'Iterating objects with sketchy length properties': true,
          'Resistant to collection length and properties changing while iterating': true,
          'countBy': [
            '{}',
            '[{}]'
          ],
          'each': [
            'context object property accessed'
          ],
          'every': [
            'Can be called with object',
            'Died on test #15',
            'context works'
          ],
          'filter': [
            'given context',
            'OO-filter'
          ],
          'find': [
            'called with context'
          ],
          'findWhere': [
            'checks properties given function'
          ],
          'groupBy': [
            '{}',
            '[{}]'
          ],
          'includes': [
            "doesn't delegate to binary search"
          ],
          'invoke': [
            'handles null & undefined'
          ],
          'map': [
            'tripled numbers with context',
            'OO-style doubled numbers'
          ],
          'max': [
            'can handle null/undefined',
            'can perform a computation-based max',
            'Maximum value of an empty object',
            'Maximum value of an empty array',
            'Maximum value of a non-numeric collection',
            'Finds correct max in array starting with num and containing a NaN',
            'Finds correct max in array starting with NaN',
            'Respects iterator return value of -Infinity',
            'String keys use property iterator',
            'Iterator context',
            'Lookup falsy iterator'
          ],
          'min': [
            'can handle null/undefined',
            'can perform a computation-based min',
            'Minimum value of an empty object',
            'Minimum value of an empty array',
            'Minimum value of a non-numeric collection',
            'Finds correct min in array starting with NaN',
            'Respects iterator return value of Infinity',
            'String keys use property iterator',
            'Iterator context',
            'Lookup falsy iterator'
          ],
          'partition': [
            'can reference the array index',
            'Died on test #8',
            'partition takes a context argument'
          ],
          'pluck': [
            '[1]'
          ],
          'reduce': [
            'can reduce with a context object'
          ],
          'reject': [
            'Returns empty list given empty array'
          ],
          'sample': [
            'behaves correctly on negative n',
            'Died on test #3'
          ],
          'some': [
            'Can be called with object',
            'Died on test #17',
            'context works'
          ],
          'where': [
            'checks properties given function'
          ],
          'Can use various collection methods on NodeLists': [
            '<span id="id2"></span>'
          ]
        },
        'Functions': {
          'debounce asap': true,
          'debounce asap cancel': true,
          'debounce asap recursively': true,
          'debounce after system time is set backwards': true,
          'debounce re-entrant': true,
          'throttle repeatedly with results': true,
          'more throttle does not trigger leading call when leading is set to false': true,
          'throttle does not trigger trailing call when trailing is set to false': true,
          'before': true,
          'bind': [
            'Died on test #2'
          ],
          'bindAll': [
            'throws an error for bindAll with no functions named'
          ],
          'debounce': [
            'incr was debounced'
          ],
          'iteratee': [
            '"bbiz"',
            '"foo"',
            '1'
          ],
          'memoize': [
            '{"bar":"BAR","foo":"FOO"}',
            'Died on test #8'
          ]
        },
        'Objects': {
          '#1929 Typed Array constructors are functions': true,
          'allKeys': [
            'is not fooled by sparse arrays; see issue #95',
            'is not fooled by sparse arrays with additional properties',
            '[]'
          ],
          'extend': [
            'extending null results in null',
            'extending undefined results in undefined'
          ],
          'extendOwn': [
            'extending non-objects results in returning the non-object value',
            'extending undefined results in undefined'
          ],
          'functions': [
            'also looks up functions on the prototype'
          ],
          'isEqual': [
            '`0` is not equal to `-0`',
            'Commutative equality is implemented for `0` and `-0`',
            '`new Number(0)` and `-0` are not equal',
            'Commutative equality is implemented for `new Number(0)` and `-0`',
            'Invalid dates are not equal',
            'false'
          ],
          'isFinite': [
            'Numeric strings are numbers',
            'Number instances can be finite'
          ],
          'isSet': [
            'Died on test #9'
          ],
          'findKey': [
            'called with context'
          ],
          'keys': [
            'is not fooled by sparse arrays; see issue #95',
            '[]'
          ],
          'mapObject': [
            'keep context',
            'called with context',
            'mapValue identity'
          ],
          'omit': [
            'can accept a predicate',
            'function is given context'
          ],
          'pick': [
            'can accept a predicate and context',
            'function is given context'
          ]
        },
        'Utility': {
          '_.escape & unescape': [
            '` is escaped',
            '` can be unescaped',
            'can escape multiple occurances of `',
            'multiple occurrences of ` can be unescaped'
          ],
          'now': [
            'Produces the correct time in milliseconds'
          ],
          'times': [
            'works as a wrapper'
          ]
        }
      };

      var mixinPrereqs = (function() {
        var aliasToReal = {
          'all': 'every',
          'allKeys': 'keysIn',
          'any': 'some',
          'collect': 'map',
          'compose': 'flowRight',
          'contains': 'includes',
          'detect': 'find',
          'extendOwn': 'assign',
          'findWhere': 'find',
          'foldl': 'reduce',
          'foldr': 'reduceRight',
          'include': 'includes',
          'indexBy': 'keyBy',
          'inject': 'reduce',
          'invoke': 'invokeMap',
          'mapObject': 'mapValues',
          'matcher': 'matches',
          'methods': 'functions',
          'object': 'zipObject',
          'pairs': 'toPairs',
          'pluck': 'map',
          'restParam': 'restArgs',
          'select': 'filter',
          'unique': 'uniq',
          'where': 'filter'
        };

        var keyMap = {
          'rest': 'tail',
          'restArgs': 'rest'
        };

        var lodash = _.noConflict();

        return function(_) {
          lodash(_)
            .defaultsDeep({ 'templateSettings': lodash.templateSettings })
            .mixin(lodash.pick(lodash, lodash.difference(lodash.functions(lodash), lodash.functions(_))))
            .value();

          lodash.forOwn(keyMap, function(realName, otherName) {
            _[otherName] = lodash[realName];
            _.prototype[otherName] = lodash.prototype[realName];
          });
          lodash.forOwn(aliasToReal, function(realName, alias) {
            _[alias] = _[realName];
            _.prototype[alias] = _.prototype[realName];
          });
          return _;
        };
      }());

      // Only excuse in Sauce Labs.
      if (!ui.isSauceLabs) {
        delete QUnit.config.excused.Functions['throttle does not trigger trailing call when trailing is set to false'];
        delete QUnit.config.excused.Utility.now;
      }
      // Load prerequisite scripts.
      document.write(ui.urlParams.loader == 'none'
        ? '<script src="' + ui.buildPath + '"><\/script>'
        : '<script data-dojo-config="async:1" src="' + ui.loaderPath + '"><\/script>'
      );
    </script>
    <script>
      if (ui.urlParams.loader == 'none') {
        mixinPrereqs(_);
        document.write([
          '<script src="../vendor/underscore/test/collections.js"><\/script>',
          '<script src="../vendor/underscore/test/arrays.js"><\/script>',
          '<script src="../vendor/underscore/test/functions.js"><\/script>',
          '<script src="../vendor/underscore/test/objects.js"><\/script>',
          '<script src="../vendor/underscore/test/cross-document.js"><\/script>',
          '<script src="../vendor/underscore/test/utility.js"><\/script>',
          '<script src="../vendor/underscore/test/chaining.js"><\/script>'
        ].join('\n'));
      }
    </script>
    <script>
      (function() {
        if (window.curl) {
          curl.config({ 'apiName': 'require' });
        }
        if (!window.require) {
          return;
        }
        // Wrap to work around tests assuming Node `require` use.
        require = (function(func) {
          return function() {
            return arguments[0] === '..' ? window._ : func.apply(null, arguments);
          };
        }(require));

        var reBasename = /[\w.-]+$/,
            basePath = ('//' + location.host + location.pathname.replace(reBasename, '')).replace(/\btest\/$/, ''),
            modulePath = ui.buildPath.replace(/\.js$/, ''),
            locationPath = modulePath.replace(reBasename, '').replace(/^\/|\/$/g, ''),
            moduleId = /\bunderscore\b/i.test(ui.buildPath) ? 'underscore' : 'lodash',
            moduleMain = modulePath.match(reBasename)[0],
            uid = +new Date;

        function getConfig() {
          var result = {
            'baseUrl': './',
            'urlArgs': 't=' + uid++,
            'waitSeconds': 0,
            'paths': {},
            'packages': [{
              'name': 'test',
              'location': '../vendor/underscore/test',
              'config': {
                // Work around no global being exported.
                'exports': 'QUnit',
                'loader': 'curl/loader/legacy'
              }
            }]
          };

          if (ui.isModularize) {
            result.packages.push({
              'name': moduleId,
              'location': locationPath,
              'main': moduleMain
            });
          } else {
            result.paths[moduleId] = modulePath;
          }
          return result;
        }

        QUnit.config.autostart = false;
        QUnit.config.excused.Functions.iteratee = true;
        QUnit.config.excused.Utility.noConflict = true;
        QUnit.config.excused.Utility['noConflict (node vm)'] = true;

        require(getConfig(), [moduleId], function(lodash) {
          _ = mixinPrereqs(lodash);
          require(getConfig(), [
            'test/collections',
            'test/arrays',
            'test/functions',
            'test/objects',
            'test/cross-document',
            'test/utility',
            'test/chaining'
          ], QUnit.start);
        });
      }());
    </script>
  </body>
</html>
